package com.knowledge.plus.java8;

import lombok.Data;
import org.junit.Test;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

/**
 * Optional
 * 示例：https://zhuanlan.zhihu.com/p/40966718
 *
 * @author TPF
 * @since 2022/12/30
 */
public class OptionalDemo {

    @Test
    public void optional() {
        // 1. 创建 Optional 实例
        Optional<String> optional1 = Optional.ofNullable(null); // value 可以为null
        Optional<String> optional2 = Optional.of("");           // value 必须非空, 否则将抛出 NullPointerException
        Optional<String> optional3 = Optional.empty();                // 创建一个空的 Optional 实例，即该 Optional 中不包含值

        // 2. 判断Optional容器中是否包含对象
        optional1.isPresent();         // 如果值存在，则返回 true，否则返回 false

        // 3. 在值存在时执行操作
        optional1.ifPresent(System.out::println);   // 如果值存在，则执行 Consumer 接口的实现代码，并且该值会作为参数传给它

        // 4. 获取Optional容器的值
        optional2.get();                // 如果值存在，则返回该值，否则抛出 NoSuchElementException
        optional3.orElse("");     // 如果值存在，则返回该值，否则返回 other
        optional3.orElse("");     // 如果值存在，则返回该值，否则返回由 Supplier 接口实现提供的对象
        optional3.orElseThrow(() -> new RuntimeException("用户没有找到")); // 如果值存在，则返回该值，否则抛出由 Supplier 接口实现提供的异常

        // 5. 过滤和映射
        optional2.filter(s -> s.length() > 3);  // 如果值存在，并且这个值匹配给定的 predicate，返回一个 Optional 用以描述这个值，否则返回一个空的 Optional
        optional2.map(s -> "return value"); // 如果有值，则对其执行调用映射函数得到返回值。如果返回值不为 null，则创建包含映射返回值的 Optional 作为 map 方法返回值，否则返回空 Optional

        // 示例：返回 "HELLO"
        Optional.ofNullable("Hello").filter(s -> s.startsWith("H")).map(String::toUpperCase).orElse("No Value");
    }

    @Test
    public void ofNullable() {
        // Optional.ofNullable：如果 Optional 中有值则将其返回，否则返回 orElse 方法传入的参数
        String value = Optional.ofNullable("985").orElse("211");
        System.out.println(value);

        // 参数也可以是对象
        Optional<User> optional = Optional.ofNullable(User.getNull());
        User temp = optional.orElse(User.getUser());
        System.out.println(temp.getUsername());

        // 创建Optional对象，差别在于of不允许参数是null，而ofNullable则无限制
        Optional<Integer> optional1 = Optional.of(561);
        System.out.println(optional1.get());

        List<String> list = null;
        List<String> newList = Optional.ofNullable(list).orElse(Arrays.asList("1", "2", "3"));
        System.out.println(newList.isEmpty());
    }

    @Test
    public void ifPresent() {
        Optional<User> user = Optional.ofNullable(User.getUser());
        // 如果 Optional 中有值 则执行操作，否则什么也不做
        user.ifPresent(u -> System.out.println("Username is: " + u.getUsername()));
    }

    /**
     * orElseGet 与 orElse 方法的区别：
     * orElse 方法：接受一个默认值作为参数，并在 Optional 对象为空时返回该默认值。重要的是，无论 Optional 对象是否为空，orElse 方法都会执行默认值的计算（如果默认值是一个需要计算或创建的对象的话）。这意味着，即使 Optional 对象包含值，默认值也可能被不必要地计算或创建。
     * orElseGet 方法：接受一个 Supplier 函数式接口作为参数，这个函数式接口在 Optional 对象为空时才会被调用，从而返回其提供的默认值。这意味着，只有在 Optional 对象为空时，默认值的计算或创建才会发生，从而提供了延迟计算的能力。
     */
    @Test
    public void orElseGet() {
        User user = Optional
                .ofNullable(User.getNull())
                // 当 Optional 中有值的时候，返回值；当 Optional 中没有值的时候，返回从该 Supplier 获得的值。
                .orElseGet(() -> User.getUserByUsername("张三"));
        System.out.println("Username is " + user.getUsername());
    }

    @Test
    public void orElseThrow() {
        User user = Optional
                .ofNullable(User.getNull())
                // 当 Optional 中有值的时候，返回值；没有值的时候会抛出异常
                .orElseThrow(() -> new RuntimeException("用户没有找到"));
        System.out.println("Username is " + user.getUsername());
    }

    @Test
    public void map() {
        /*
         * 如果当前 Optional 为 Optional.empty，则依旧返回 Optional.empty；
         * 否则返回一个新的 Optional，该 Optional 包含的是函数 mapper 在以 value 作为输入时的输出值。
         */
        Optional<String> optional = Optional
                .ofNullable(User.getUser())
                .map(user -> user.getUsername());
        System.out.println("Username is: " + optional.orElse("Unknown"));

        Optional<String> username = Optional
                .ofNullable(User.getUserByUsername("abc__DEF_G"))
                .map(user -> user.getUsername())
                .map(name -> name.toLowerCase())
                .map(name -> name.replace('_', ' '));
        System.out.println("Username is: " + username.orElse("Unknown"));
    }

    @Test
    public void filter() {
        /*
         * filter 方法接受一个 Predicate 来对 Optional 中包含的值进行过滤。
         * 如果包含的值满足条件，那么还是返回这个 Optional；否则返回 Optional.empty
         */
        Optional<String> username = Optional
                .ofNullable(User.getUserByUsername("孙悟空"))
                .filter(user -> user.getUsername().equals("猪八戒"))
                .map(user -> user.getUsername());
        System.out.println("Username is: " + username.orElse("Unknown"));
    }

    /* Java9 对 Optional 增强 */

}

@Data
class User {
    String username;

    User() {
        this.username = "卡卡罗特";
    }

    User(String username) {
        this.username = username;
    }

    static User getUser() {
        return new User();
    }

    static User getUserByUsername(String username) {
        return new User(username);
    }

    static User getNull() {
        return null;
    }
}
